/*
 * ncurses_drawer.cpp
 *
 *  Created on: 2011-5-23
 *      Author: chenl
 */

#include "ncurses_drawer.h"
#include "common/common_def.h"
#include "processmgt.h"
#include "ps_error.h"
#include "config.h"
#ifndef WIN32
#include <ncurses.h>
#endif


#define RUNQUEUE_MONITOR_TITLE	"runqueue monitor"
#define WAITQUEUE_MONITOR_TITLE	"waitqueue monitor"

CNcursesDrawer::CNcursesDrawer()
	: columns_(0), rows_(0), pRunQueueMointor_(NULL), pWaitQueueMointor_(NULL)
{

}

CNcursesDrawer::~CNcursesDrawer()
{

}

int32_t CNcursesDrawer::Initialize()
{
	initscr();
	cbreak();
	refresh();

	getmaxyx(stdscr, rows_, columns_);
	int monitorHeight = rows_ / 2 - 1;
	int monitorWidth = columns_;

	int currentRow = 0;

	pRunQueueMointor_ = new CMonitor(monitorHeight, monitorWidth, currentRow, 0, RUNQUEUE_MONITOR_TITLE);
	if (NULL == pRunQueueMointor_)
	{
		return E_PS_CREATE_MONITOR_FAIL;
	}
	currentRow += monitorHeight;
	pWaitQueueMointor_ = new CMonitor(monitorHeight, monitorWidth, currentRow, 0, WAITQUEUE_MONITOR_TITLE);
	if (NULL == pRunQueueMointor_)
	{
		return E_PS_CREATE_MONITOR_FAIL;
	}
	currentRow += monitorHeight;

	int32_t ret = pRunQueueMointor_->Initialize();
	if (0 > ret)
	{
		return ret;
	}
	ret = pWaitQueueMointor_->Initialize();
	if (0 > ret)
	{
		return ret;
	}

	switch(GET_PARAMCONFIG_INSTANCE().GetScheduler())
	{
	case enmScheduler_O_N:
		getPCBInfoString_ = _GetPCBInfoString_O_N;
		break;
	case enmScheduler_O_1:
		getPCBInfoString_ = _GetPCBInfoString_O_1;
		break;
	case enmScheduler_BFS:
	case enmScheduler_CFS:
	default:
		return E_PS_MISCONFIGED;
	}
	refresh();
	return S_OK;
}

int32_t CNcursesDrawer::Uninitialize()
{
	endwin();
	return S_OK;
}

void CNcursesDrawer::Update(void *p)
{
	CProcessMgt& processMgt = GET_PROCESSMGT_INSTANCE();
	char szPCBInfoString[MAX_ROW][MAX_COLUMN] = {{0}};
	unsigned len = columns_;

	CTask* pTask = NULL;
	unsigned i = 0;
	int highlight = 0;
	for_each_task(processMgt, RunQueue, pTask)
	{
		assert(NULL != pTask);
		if (pTask == GET_PROCESSMGT_INSTANCE().CurrentTask())
			highlight = i;
		len = columns_;
		getPCBInfoString_(*pTask, szPCBInfoString[i++], len);
	}
	pRunQueueMointor_->Update(szPCBInfoString, i, len, 0, highlight);
	i = 0;
	for_each_task(processMgt, WaitQueue, pTask)
	{
		assert(NULL != pTask);
		len = columns_;
		getPCBInfoString_(*pTask, szPCBInfoString[i++], len);
	}
	pWaitQueueMointor_->Update(szPCBInfoString, i, len, 0 , -1);
}

void CNcursesDrawer::_GetPCBInfoString_O_N(const CTask& task, char* szContent, unsigned& len)
{
	unsigned length = 0;
	int offset = 0;
	char* p = szContent;
	snprintf(p, len, "%d", task.task.pid);
	FormatPCBInfoString(p, PCB_INFO_PID, offset);
	assert(offset >= 0);
	int n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}
	p += n;
	snprintf(p, len, "%ld", task.task.state);
	FormatPCBInfoString(p, PCB_INFO_STATE, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}
	p += n;
	snprintf(p, len, "%ld", task.task.need_resched);
	FormatPCBInfoString(p, PCB_INFO_NEED_RESCHED, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}
	p += n;
	snprintf(p, len, "%lu", task.task.policy);
	FormatPCBInfoString(p, PCB_INFO_POLICY, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}
	p += n;
	snprintf(p, len, "%ld", task.task.counter);
	FormatPCBInfoString(p, PCB_INFO_COUNTER, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}
	p += n;
	snprintf(p, len, "%ld", task.task.nice);
	n = strlen(p);
	length += n;
	assert(length <= len);
	len = length;
}

void CNcursesDrawer::_GetPCBInfoString_O_1(const CTask& task, char* szContent, unsigned& len)
{
	unsigned length = 0;
	int offset = 0;
	char* p = szContent;

	snprintf(p, len, "%d", task.task.pid);
	FormatPCBInfoString(p, PCB_INFO_PID, offset);
	assert(offset >= 0);
	int n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}

	p += n;
	snprintf(p, len, "%ld", task.task.state);
	FormatPCBInfoString(p, PCB_INFO_STATE, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}

	p += n;
	snprintf(p, len, "%ld", task.task.need_resched);
	FormatPCBInfoString(p, PCB_INFO_NEED_RESCHED, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}

	p += n;
	snprintf(p, len, "%lu", task.task.policy);
	FormatPCBInfoString(p, PCB_INFO_POLICY, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}

	p += n;
	snprintf(p, len, "%d", task.task.prio);
	FormatPCBInfoString(p, PCB_INFO_PRIO, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}

	p += n;
	snprintf(p, len, "%d", task.task.static_prio);
	FormatPCBInfoString(p, PCB_INFO_STATIC_PRIO, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}

	p += n;
	snprintf(p, len, "%d", task.task.time_slice);
	FormatPCBInfoString(p, PCB_INFO_TIME_SLICE, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}

	p += n;
	snprintf(p, len, "%lu", task.task.sleep_time);
	FormatPCBInfoString(p, PCB_INFO_SLEEP_TIME, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}

	p += n;
	snprintf(p, len, "%lu", task.task.sleep_avg);
	FormatPCBInfoString(p, PCB_INFO_SLEEP_AVG, offset);
	assert(offset >= 0);
	n = strlen(p);
	length += n;
	if (length >= len)
	{
		return;
	}

	p += n;
	snprintf(p, len, "0x%08x", (unsigned)task.task.array);
	n = strlen(p);
	length += n;
	assert(length <= len);
	len = length;
}

void CNcursesDrawer::FormatPCBInfoString(char* szContent, const char* szColumnName, int& offset)
{
	unsigned contentLen = strlen(szContent);
	unsigned columnNameLen = strlen(szColumnName);
	offset = columnNameLen - contentLen + 1;

	if (0 >= offset)
	{
		return ;
	}

	if (8 < offset)
	{
		szContent[contentLen++] = '\t';
		offset++;
	}

	szContent[contentLen] = '\t';
	szContent[contentLen + 1] = '\0';
}

